/**
 * @file CLambdaAlg.cpp Implementácia triedy CLambdaAlg.
 *
 * @author Martin Kosorinský.
 * @date 1.4.2009.
 */

#include "CLambdaAlg.hpp"

big CLambdaAlg::stepLimit = NULL;
big CLambdaAlg::paramA = NULL;
big CLambdaAlg::paramB = NULL;


void CLambdaAlg::setLambdaParams(const big a, const big b) {
    if (paramA == NULL)
        paramA = mirvar(0);                         // alokacia dolnej hranice prehladavaneho intervalu
    if (paramB == NULL)
        paramB = mirvar(0);                         // alokacia hornej hranice prehladavaneho intervalu
    if (stepLimit == NULL)
        stepLimit = mirvar(0);

    if (a != NULL)
        copy(a, paramA);
    else
        zero(paramA);                               // nastavenie default na dolna hranica = 0

    if (b != NULL)
        copy(b, paramB);
    else
        decr(ord, 1, paramB);                       // hornu hranicu nastavime na ord-1 -> teda prehladavame celu grupu
}

void CLambdaAlg::freeLambdaParams() {
    if (paramA != NULL) {
        mirkill(paramA);
        paramA = NULL;
    }
    if (paramB != NULL) {
        mirkill(paramB);
        paramB = NULL;
    }
    if (stepLimit != NULL) {
        mirkill(stepLimit);
        stepLimit = NULL;
    }
}

bool CLambdaAlg::computeLogarithm(big vysledok) {

    tracer << "Pollard-Lambda:" << endl;
    if (!isInitialized()) {
        cerr << "Nie je inicializovaný kontext!" << endl;
        return false;
    }

    setLambdaParams();                              // alokovanie a nastavenie statickych parametrov vypoctu

    big paramN = mirvar(0);                         // pocet pasci = pocet skokov skrotenej kengury
    subtract(paramB, paramA, bigBuffer[0]);         // pomocny vypocet sirky prehladavaneho intervalu
    nroot(bigBuffer[0], 2, paramN);                 // pocet pasci nastavime na odmocninu dlzky prehladavaneho intervalu
    sftbit(paramN, 1, stepLimit);                   // priemerna hodnota skokov bude odmocnina z dlzky intervalu.
    // TODO: nastavit stepLimit na najblizsiu mocninu 2 kvoli rychlosti

    tracer << "Pracovny interval: (" << paramA << "," << paramB << ")" << endl;
    tracer << "Sirka pracovneho intervalu: " << bigBuffer[0] << endl;
    tracer << "Parameter N: " << paramN << endl;
    tracer << "Step limit: " << stepLimit << endl;

    epoint * T = epoint_init();                     // bod, predstavujuci skrotenu kenguru
    big dT = mirvar(0);                             // ubehnuta vzdialenost skrotenej kengury
    ecurve_mult(paramB, G, T);                      // skrotena kengura zacina beh na konci prehladavaneho intervalu

    for (zero(bigBuffer[2]); compare(bigBuffer[2], paramN) < 1 ; incr(bigBuffer[2], 1, bigBuffer[2])) {
        makeStep(T, dT);                            // deterministicky pseudonahodny skok skrotenej kengury
    }
    tracer << "Skrotena kengura sa zakopala v  " << T << " (distance = " << dT << ")" << endl;

    epoint * W = epoint_init();                     // divoka kengura
    big dW = mirvar(0);                             // ubehnuta vzdialenost divokej kengury
    epoint_copy(H, W);                              // divoka kengura zacina na bode, ktoreho DL hladame
    big distT = mirvar(0);                          // pomocna premenna pre vypocty
    add(paramB, dT, distT);                         // do dist sme ulozili dn + paramB
    bool catchedWild = false;                       // flag signalizujuci chytenie divokej kengury

    for (int i = 1; i <= 10; i++) {                 // sekvencne vypustime 10 divokych kengur
        while (!catchedWild) {
            if (epoint_comp(W, T)) {                    // polohu divokej kengury porovname so zakopanou
                tracer << "DIVOKA KENGURA JE V PASCI!" << " (distance = " << dW << ")" << endl;

                subtract(distT, dW, vysledok);      // vypocet indexu
                divide(vysledok, ord, ord);         // index modulujeme radom grupy (preistotu :P)
                catchedWild = true;                 // signalizujeme chytenie kengury
                break;
            }
            makeStep(W, dW);                        // vykoname skok divokej kengury

            subtract(distT, paramA , vysledok);     // overujeme, ci divoka kengura nie je uz prilis daleko za zakopanou
            if (compare(vysledok, dW) == -1) {      // ak je TRUE, divoka kengura zdrhla .. nema vyznam pokracovat vo vypocte
                tracer << endl << "DIVOKA KENGURA ZDRHLA!" << endl;
                catchedWild = false;                // kengura nebola chytena
                break;                              // prerusenie slucky -> dealokacia a koniec vypoctu s neuspechom
            }
        }
        if (catchedWild)                            // ak bola kengura chytena, prerusujeme hladanie
            break;

        // ked sme tu, tak kengura zdrhla .. musime zalozit nove hladanie s novou divokou kengurou
        // TODO: tu by bolo vhodne osetrit posun novej kengury
        tracer << "Vypustame dalsiu divoku kenguru." << endl;
        epoint_copy(H, W);
        for (int n = 0; n < i; n++)
            ecurve_add(G, W);
        zero(dW);
        incr(dW, i, dW);
    }
    if (!catchedWild) {
        tracer << endl << "NEUSPECH: vsetky kengury zdrhli!!! Koncime vypocet." << endl;
    }

    freeLambdaParams();
    mirkill(paramN);
    mirkill(dT);
    epoint_free(T);
    mirkill(distT);
    mirkill(dW);
    epoint_free(W);
    return catchedWild;
}

inline void CLambdaAlg::getStepSize(const epoint * P, big stepSize) {
    epoint_get((epoint*)P, bigBuffer[0], bigBuffer[1]);
    add(bigBuffer[0], bigBuffer[1], bigBuffer[0]);
    divide(bigBuffer[0], stepLimit, stepLimit);
    incr(bigBuffer[0], 1, stepSize);
}

inline void CLambdaAlg::makeStep(epoint * P, big dm) {
    epoint * X = epoint_init();
    getStepSize(P, bigBuffer[0]);
    ecurve_mult(bigBuffer[0], G, X);
    ecurve_add(X, P);
    add(dm, bigBuffer[0], dm);
    epoint_free(X);
}
